package comlib.chisel

import chisel3._

/** Counter Table
  */
class Counters[T<:Bits with Num[T]] (num:Int, init:T=0.U.asInstanceOf[T]) {

  // ---- Used for inner data encapsulation
  class ValidIndexData( val vld:Bool, val id:UInt, val value:T )

//   val cnts = Wire( Vec(num, chiselTypeOf(init)) )
  val cnts = RegInit(VecInit(Seq.fill(num)(init)))
  protected var isLoad = false

  protected var incs : Seq[ValidIndexData] = Seq()       // Increase commands
  protected var decs : Seq[ValidIndexData] = Seq()       // Decrease commands

  val ofErr = WireInit(false.B)       // Overflow error flag
  val ofId  = WireInit(0.U)           // Overflow index
  val ufErr = WireInit(false.B)       // Underflow error flag
  val ufId  = WireInit(0.U)           // Unerflow index

  def load(cnts:Vec[T]) = {
    require( cnts.size == num )
    this.cnts := cnts
    isLoad = true
    this
  }


  /** 添加计数器“加”控制信号
    * @param en: "加"使能
    * @param id: 计数器序号信号
    * @param ival: 计数器增加量信号
    */
  def inc( en:Bool, id:UInt, ival:T ) = {
    this.incs = incs :+ new ValidIndexData( en, id, ival )
    this
  }

  /** 添加计数器“减”控制信号
    * @param en: "减"使能
    * @param id: 计数器序号信号
    * @param dval: 计数器减少量信号
    */
  def dec( en:Bool, id:UInt, dval:T ) = {
    this.decs = decs :+ new ValidIndexData( en, id, dval )
    this
  }


    /*根据incs和decs的命令，进行实际的Counter值计算和更新*/
    def count() = {

        require( incs.size <= 1, s"当前仅支持一个加口. But the size=${incs.size}" )
        require( decs.size <= 1, s"当前仅支持一个减口, But the size=${decs.size}" )

        // if( !isLoad ){
        //     cnts := RegInit(VecInit(Seq.fill(num)(init)))
        //     isLoad = true
        // }

        val inc_i = incs(0)
        val dec_i = decs(0)

        //---- Initial value
        ofErr := false.B
        ufErr := false.B
        ofId := inc_i.id
        ufId := dec_i.id

        when( inc_i.vld & dec_i.vld & inc_i.id===dec_i.id ){

            val ret = Counter[T]( init=init ).load( cnts( inc_i.id ) ).inc( true.B, inc_i.value ).dec( true.B, dec_i.value ).value( false, false, false )
            cnts( inc_i.id ) := ret._1
            ofErr := ret._2
            ufErr := ret._3

        }.otherwise{

            when( inc_i.vld ){
                val iret = Counter( init=init ).load( cnts(inc_i.id) ).inc( true.B, inc_i.value ).value( false, false, false )
                cnts( inc_i.id ) := iret._1
                ofErr := iret._2
            }

            when( dec_i.vld ){
                val iret = Counter( init=init ).load( cnts(dec_i.id) ).dec( true.B, dec_i.value ).value(false, false, false)
                cnts( dec_i.id ) := iret._1
                ufErr := iret._2
            }

        }

        this
    }

}

object Counters {

    def apply[T<:Bits with Num[T]](num:Int, bits:Int) = new Counters( num, 0.U(bits.W).asInstanceOf[T] )

    def apply[T<:Bits with Num[T]](v:Vec[T]) = new Counters( v.size, 0.U(v.head.getWidth.W).asInstanceOf[T] ).load(v)
}